#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <iostream>
#include <functional>

#include <workflow/WFFacilities.h>
#include <workflow/WFHttpServer.h>
#include <workflow/RedisMessage.h>
#include <nlohmann/json.hpp>

using std::cout;
using std::endl;
using std::string;

//通过构造函数初始化计数器的值 
static WFFacilities::WaitGroup waitGroup(1);

void sighandler(int num)
{
    printf("sig %d is coming\n", num);
    //将计数器的值减1
    waitGroup.done();
}

class MuploadServer
{
public:
    MuploadServer(int cnt)
    : _waitGroup(cnt)
    , _server(std::bind(&MuploadServer::process, this, std::placeholders::_1))
    {}

    void start(unsigned short port);

private:
    void process(WFHttpTask *);
    void init(WFHttpTask *, const string & username);
    void uppart(WFHttpTask *, const string & uploadid, const string & chunkidx);
    void complete(WFHttpTask *, const string & uploadid);
    string produceUploadID(const string & username);

private:
    WFFacilities::WaitGroup _waitGroup;
    WFHttpServer _server;
};

void MuploadServer::process(WFHttpTask * serverTask)
{
    printf("MuploadServer::process is running.\n");
    //0. 对任务的状态进行检测
    int state = serverTask->get_state();
    printf("state: %d\n", state);
    int error = serverTask->get_error();
    if(state != WFT_STATE_TOREPLY) {
        printf("error ocurrs: %s\n", WFGlobal::get_error_string(state, error));
        return;
    }

    auto req = serverTask->get_req();
    string method = req->get_method();
    string uri = req->get_request_uri();
    string path = uri.substr(0, uri.find("?"));
    if(method == "POST" && path == "/file/mupload/init") {
        //1. 解析username
        string usernameKV = uri.substr(uri.find("?") + 1);
        string username = usernameKV.substr(usernameKV.find("=") + 1);
        init(serverTask, username);
    } else if(method == "POST" && path == "/file/mupload/uppart") {
        // /file/mupload/uppart?uploadid=liubei20231214150447&chunkidx=0
        //2. 解析uploadID和chunkidx
        string uploadIDKV = uri.substr(0, uri.find("&"));
        string chunkidxKV = uri.substr(uri.find("&") + 1);
        string uploadID = uploadIDKV.substr(uploadIDKV.find("=") + 1);
        string chunkidx = chunkidxKV.substr(chunkidxKV.find("=") + 1);
        uppart(serverTask, uploadID, chunkidx);
    } else if(method == "GET" && path == "/file/mupload/complete") {
        // /file/mupload/complete?uploadid=liubei20231214150447
        //3. 解析uploadID
        string uploadID = uri.substr(uri.find("=") + 1);
        complete(serverTask, uploadID);
    }
}

void MuploadServer::init(WFHttpTask * serverTask, const string & username)
{
    printf("init section\n");
    //2. 生成文件上传的唯一标识uploadID
    string uploadID = produceUploadID(username);
    cout << "uploadID:" << uploadID << endl;
    //3. 获取分片信息
    auto req = serverTask->get_req();
    const void * body = nullptr;
    size_t sz = 0;
    req->get_parsed_body(&body, &sz);
    string strJson((const char *)body, sz);
    using Json = nlohmann::json;
    Json fileInfo = Json::parse(strJson);//反序列化
    string filename = fileInfo["filename"];
    string filehash = fileInfo["filehash"];
    size_t filesize = fileInfo["filesize"];
    size_t chunksize = 100 * 1024 * 1024;
    size_t chunkcnt = filesize / chunksize + (filesize % chunksize ? 1 : 0);
    fileInfo["chunksize"] = chunksize;
    fileInfo["chunkcnt"] = chunkcnt;
    fileInfo["uploadid"] = uploadID;
    //4. 生成响应
    serverTask->get_resp()->append_output_body(fileInfo.dump());//序列化
    //5. 保存到Redis中
    string url("redis://127.0.0.1:6379");
    auto redisTask = WFTaskFactory::create_redis_task(url, 1, nullptr);
    string command("HSET");
    std::vector<string> params{
        uploadID,
        "filename", filename,
        "filehash", filehash,
        "filesize", std::to_string(filesize),
        "chunksize", std::to_string(chunksize),
        "chunkcnt", std::to_string(chunkcnt)
    };
    redisTask->get_req()->set_request(command, params);
    series_of(serverTask)->push_back(redisTask);
}

void MuploadServer::uppart(WFHttpTask * serverTask, 
                           const string & uploadid, 
                           const string & chunkidx) {
    printf("uppart section\n");
    cout << "uploadid: " << uploadid << endl
         << "chunkidx: " << chunkidx << endl;
    //1. 获取分片内容
    auto req = serverTask->get_req();
    auto resp = serverTask->get_resp();
    const void * body = nullptr;
    size_t sz = 0;
    req->get_parsed_body(&body, &sz);
    //2. 创建目录, 并将分片内容写入文件
    mkdir(uploadid.c_str(), 0755);
    string fragment = uploadid + "/" + chunkidx;
    int fd = open(fragment.c_str(), O_CREAT|O_RDWR, 0644);
    if(fd < 0) {
        perror("open");
        resp->append_output_body("fragment upload failed.");
        return;
    }
    write(fd, body, sz);
    close(fd);
    resp->append_output_body("frgament upload success");
    //3. 将分片记录信息写入Redis中
    string url("redis://127.0.0.1:6379");
    auto redisTask = WFTaskFactory::create_redis_task(url, 1, nullptr);
    string command("HSET");
    std::vector<string> params{
        uploadid,
        "chunkidx_" + chunkidx, "1"
    };
    redisTask->get_req()->set_request(command, params);
    series_of(serverTask)->push_back(redisTask);
}

void MuploadServer::complete(WFHttpTask * serverTask, const string & uploadid)
{
    printf("complete section\n");
    cout << "uploadid: " << uploadid << endl;
    
    //2. 从Redis中获取所有的与uploadid相关字段
    string url("redis://127.0.0.1:6379");
    auto redisTask = WFTaskFactory::create_redis_task(url, 1, 
    [serverTask](WFRedisTask * redistask){
        //对Redis任务的状态进行检测
        int state = redistask->get_state();
        int error = redistask->get_error();
        if(state != WFT_STATE_SUCCESS) {
            printf("%s\n", WFGlobal::get_error_string(state, error));
            return;
        }
        protocol::RedisValue result;
        redistask->get_resp()->get_result(result);
        if(result.is_array()) {//结果集应该是一个数组
            size_t chunkcnt = -1;
            size_t chunkcur = 0;
            for(size_t i = 0; i < result.arr_size(); i += 2) {
                string key = result.arr_at(i).string_value();
                if(key == "chunkcnt") {
                    chunkcnt = atoi(result.arr_at(i + 1).string_value().c_str());
                }
                if(key.substr(0, 9) == "chunkidx_") {
                    ++chunkcur;
                }
            }
            printf("chunkcnt: %ld, chunkcur: %ld\n", chunkcnt, chunkcur);
            if(chunkcnt == chunkcur) {
                //... 合并文件, 计算hash值, 比较hash值
                serverTask->get_resp()->append_output_body("Upload File Success");
            } else {
                //验证出现错误情况
                serverTask->get_resp()->append_output_body("Upload File Failed");
            }
        } else {
            //验证出现错误情况
            serverTask->get_resp()->append_output_body("Upload File Failed");
        }
    });
    string command("HGETALL");
    std::vector<string> params{uploadid};
    redisTask->get_req()->set_request(command, params);
    series_of(serverTask)->push_back(redisTask);
}

void MuploadServer::start(unsigned short port)
{
    if(_server.start(port) == 0) {
        _waitGroup.wait();
        _server.stop();
    } else {
        printf("MuploadServer start failed.\n");
    }
}

string MuploadServer::produceUploadID(const string & username)
{
    time_t secs = time(nullptr);
    struct tm * ptm = localtime(&secs);
    char buff[15] = {0};
    sprintf(buff, "%04d%02d%02d%02d%02d%02d", 
            ptm->tm_year + 1900,
            ptm->tm_mon + 1,
            ptm->tm_mday,
            ptm->tm_hour,
            ptm->tm_min,
            ptm->tm_sec);
    return username + buff;
}


void test0()
{
    MuploadServer server(1);
    server.start(8888);
}


int main()
{
    test0();
    printf("exit main\n");
    return 0;
}
